/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.core;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;
import javax.swing.Action;
import javax.swing.KeyStroke;
import javax.swing.event.*;
import javax.swing.text.Keymap;
import org.openide.TopManager;
import org.openide.filesystems.*;
import org.openide.loaders.XMLDataObject;
import org.openide.util.Utilities;
import org.openide.util.actions.SystemAction;
import org.openide.util.HelpCtx;
/**
*
* @author Ian Formanek
*/
public class ShortcutsEditor extends javax.swing.JPanel {
private static final java.util.ResourceBundle bundle = org.openide.util.NbBundle.getBundle(ShortcutsEditor.class);
public static final String PROPERTIES_FILE = "Shortcuts"; // NOI18N
public static final String DEFAULT_KEYS_FILE = "Default"; // NOI18N
public static final String USER_KEYS_FILE = "UserDefined"; // NOI18N
private static final String SHORTCUTS_FOLDER = "Shortcuts"; // NOI18N
private static final String KEYS_EXT = "keys"; // NOI18N
private static final String XML_SHORTCUTS = "Shortcuts"; // NOI18N
private static final String XML_DESCRIPTION = "Description"; // NOI18N
private static final String XML_BINDING = "Binding"; // NOI18N
private static final String ATTR_DESCRIPTION_NAME = "name"; // NOI18N
private static final String ATTR_BINDING_KEY = "key"; // NOI18N
private static final String ATTR_BINDING_ACTION = "action"; // NOI18N
private static final String PROP_CURRENT_KEYFILE = "current"; // NOI18N
private ShortcutsPanel shortcutsPanel;
private ActionsPanel actionsPanel;
private java.awt.Component currentPanel;
private boolean modified = false;
/** Creates new form ShortcutsEditor */
public ShortcutsEditor() {
actionsPanel = new ActionsPanel (true, this);
shortcutsPanel = new ShortcutsPanel (this);
initComponents ();
radioPanel.setBorder (new javax.swing.border.TitledBorder (
new javax.swing.border.EtchedBorder (), bundle.getString ("ShortcutsEditor.typeTitle")));
javax.swing.ButtonGroup group = new javax.swing.ButtonGroup ();
group.add(actionsButton);
group.add(shortcutsButton);
actionsButton.setSelected (true);
updateDynamicContent ();
requestFocus ();
radioPanel.requestFocus();
actionsButton.requestFocus();
HelpCtx.setHelpIDString (this, ShortcutsEditor.class.getName ());
}
public java.awt.Dimension getPreferredSize() {
return new java.awt.Dimension (370, 600);
}
void setModified (boolean value) {
modified = value;
}
public boolean isModified () {
return modified;
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the FormEditor.
*/
private void initComponents () {//GEN-BEGIN:initComponents
radioPanel = new javax.swing.JPanel ();
actionsButton = new javax.swing.JRadioButton ();
shortcutsButton = new javax.swing.JRadioButton ();
setLayout (new java.awt.BorderLayout ());
setBorder (new javax.swing.border.EmptyBorder(new java.awt.Insets(8, 8, 8, 8)));
radioPanel.setLayout (new java.awt.GridBagLayout ());
java.awt.GridBagConstraints gridBagConstraints1;
actionsButton.setContentAreaFilled (actionsButton.isContentAreaFilled ());
actionsButton.setText (org.openide.util.NbBundle.getBundle(ShortcutsEditor.class).getString("ShortcutsEditor.actionsButton.text"));
actionsButton.addActionListener (new java.awt.event.ActionListener () {
public void actionPerformed (java.awt.event.ActionEvent evt) {
actionsButtonActionPerformed (evt);
}
}
);
gridBagConstraints1 = new java.awt.GridBagConstraints ();
gridBagConstraints1.gridwidth = 0;
gridBagConstraints1.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints1.insets = new java.awt.Insets (0, 8, 0, 8);
gridBagConstraints1.weightx = 1.0;
radioPanel.add (actionsButton, gridBagConstraints1);
shortcutsButton.setText (org.openide.util.NbBundle.getBundle(ShortcutsEditor.class).getString("ShortcutsEditor.shortcutsButton.text"));
shortcutsButton.addActionListener (new java.awt.event.ActionListener () {
public void actionPerformed (java.awt.event.ActionEvent evt) {
shortcutsButtonActionPerformed (evt);
}
}
);
gridBagConstraints1 = new java.awt.GridBagConstraints ();
gridBagConstraints1.gridwidth = 0;
gridBagConstraints1.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints1.insets = new java.awt.Insets (0, 8, 0, 8);
gridBagConstraints1.weightx = 1.0;
radioPanel.add (shortcutsButton, gridBagConstraints1);
add (radioPanel, java.awt.BorderLayout.NORTH);
}//GEN-END:initComponents
private void shortcutsButtonActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_shortcutsButtonActionPerformed
updateDynamicContent ();
}//GEN-LAST:event_shortcutsButtonActionPerformed
private void actionsButtonActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_actionsButtonActionPerformed
updateDynamicContent ();
}//GEN-LAST:event_actionsButtonActionPerformed
private void updateDynamicContent () {
if (actionsButton.isSelected()) {
if (currentPanel != actionsPanel) {
if (currentPanel != null) remove (currentPanel);
currentPanel = actionsPanel;
actionsPanel.updateTree ();
add (currentPanel, java.awt.BorderLayout.CENTER);
revalidate ();
repaint ();
}
} else {
if (currentPanel != shortcutsPanel) {
if (currentPanel != null) remove (currentPanel);
currentPanel = shortcutsPanel;
add (currentPanel, java.awt.BorderLayout.CENTER);
revalidate ();
repaint ();
}
}
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JPanel radioPanel;
private javax.swing.JRadioButton actionsButton;
private javax.swing.JRadioButton shortcutsButton;
// End of variables declaration//GEN-END:variables
// -----------------------------------------------------------------------------
// Static methods
static String getKeyStrokeName (KeyStroke stroke) {
Keymap map = TopManager.getDefault ().getGlobalKeymap ();
Action action = map.getAction (stroke);
if (action instanceof SystemAction) {
String name = ((SystemAction)action).getName ();
name = Utilities.replaceString (name, "&", ""); // remove mnemonics marker // NOI18N
name = Utilities.replaceString (name, "...", ""); // remove trailing "..." // NOI18N
return getKeyText (stroke) + " [" + name + "]"; // NOI18N
} else {
if (action == null) return getKeyText (stroke);
else return getKeyText (stroke) + " [" + action.getValue (Action.NAME) + "]"; // NOI18N
}
}
static String getActionName (SystemAction action) {
String name = Utilities.replaceString (action.getName (), "&", ""); // remove mnemonics marker // NOI18N
name = Utilities.replaceString (name, "...", ""); // remove trailing "..." // NOI18N
Keymap map = TopManager.getDefault ().getGlobalKeymap ();
KeyStroke[] strokes = map.getKeyStrokesForAction(action);
if (strokes.length > 0) {
name = name + " ["; // NOI18N
for (int i = 0; i < strokes.length; i++) {
name = name + getKeyText (strokes[i]);
if (i != strokes.length - 1) {
name = name + ", "; // NOI18N
}
}
return name + "]"; // NOI18N
} else {
return name;
}
}
static String getKeyText (int keyCode, int modifiers) {
String modifText = java.awt.event.KeyEvent.getKeyModifiersText(modifiers);
if ("".equals (modifText)) return java.awt.event.KeyEvent.getKeyText(keyCode); // NOI18N
else {
if ((keyCode == KeyEvent.VK_ALT) || (keyCode == KeyEvent.VK_ALT_GRAPH) || (keyCode == KeyEvent.VK_CONTROL) || (keyCode == KeyEvent.VK_SHIFT)) {
return modifText + "+"; // in this case the keyCode text is also among the modifiers // NOI18N
} else {
return modifText + "+" + java.awt.event.KeyEvent.getKeyText(keyCode); // NOI18N
}
}
}
static String getKeyText (KeyStroke stroke) {
String modifText = java.awt.event.KeyEvent.getKeyModifiersText(stroke.getModifiers ());
if ("".equals (modifText)) return java.awt.event.KeyEvent.getKeyText(stroke.getKeyCode ()); // NOI18N
else return modifText + "+" + java.awt.event.KeyEvent.getKeyText(stroke.getKeyCode ()); // NOI18N
}
public static void main (String[] args) {
javax.swing.JFrame fr = new javax.swing.JFrame ();
fr.getContentPane().setLayout(new java.awt.BorderLayout ());
fr.getContentPane().add (new ShortcutsEditor (), java.awt.BorderLayout.CENTER);
fr.show ();
}
static org.w3c.dom.Document parseKeysFile (java.net.URL url) throws org.xml.sax.SAXException, java.io.IOException {
return XMLDataObject.parse(url, new org.xml.sax.ErrorHandler () {
public void error (org.xml.sax.SAXParseException e) {
// [PENDING]
}
public void warning (org.xml.sax.SAXParseException e) {
// [PENDING]
}
public void fatalError (org.xml.sax.SAXParseException e) {
// [PENDING]
}
}
);
}
// -----------------------------------------------------------------------------
// public static methods
public static void saveCustomKeys () throws java.io.IOException {
Keymap map = TopManager.getDefault ().getGlobalKeymap ();
KeyStroke[] strokesArray = map.getBoundKeyStrokes();
String indent = " "; // NOI18N
org.openide.filesystems.FileSystem systemFS = TopManager.getDefault().getRepository().getDefaultFileSystem ();
boolean defaultsUsed = false;
HashMap moduleKeyFiles = new HashMap (11);
FileObject shortcutsFolder = null;
try {
shortcutsFolder = systemFS.find (SHORTCUTS_FOLDER, null, null);
if (shortcutsFolder == null) {
shortcutsFolder = systemFS.getRoot ().createFolder (SHORTCUTS_FOLDER);
}
} catch (Exception e) {
if (Boolean.getBoolean ("netbeans.debug.exceptions")) e.printStackTrace(); // NOI18N
}
if (shortcutsFolder == null) return; // cannot save [PENDING - notify]
FileLock lock = null;
try {
FileObject mainFO = systemFS.find (SHORTCUTS_FOLDER, USER_KEYS_FILE, KEYS_EXT);
if (mainFO == null) {
mainFO = shortcutsFolder.createData (USER_KEYS_FILE, KEYS_EXT);
}
java.io.PrintWriter pw = new java.io.PrintWriter (mainFO.getOutputStream (lock = mainFO.lock ()));
pw.println ("<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>"); // NOI18N
pw.println ();
pw.println ("<" + XML_SHORTCUTS + ">"); // NOI18N
pw.println (indent + "<" + XML_DESCRIPTION + " " + ATTR_DESCRIPTION_NAME + "=" + "\"User Defined Shortcuts\" />"); // NOI18N
pw.println ();
for (int i = 0; i < strokesArray.length; i++) {
String stroke = Utilities.keyToString (strokesArray[i]);
String action = map.getAction (strokesArray[i]).getClass ().getName ();
pw.println (indent + "<" + XML_BINDING + " " + ATTR_BINDING_KEY + "=\"" + stroke + "\"" + " " + ATTR_BINDING_ACTION +"=\"" + action + "\" />"); // NOI18N
}
pw.println ("</" + XML_SHORTCUTS + ">"); // NOI18N
pw.close ();
} catch (Exception e) {
if (Boolean.getBoolean ("netbeans.debug.exceptions")) e.printStackTrace(); // NOI18N
} finally {
if (lock != null) {
lock.releaseLock ();
}
}
FileObject propsFO = systemFS.find (SHORTCUTS_FOLDER, PROPERTIES_FILE, "properties"); // NOI18N
FileLock propsLock = null;
try {
if (propsFO == null) {
propsFO = shortcutsFolder.createData (PROPERTIES_FILE, "properties"); // NOI18N
}
propsLock = propsFO.lock ();
Properties props = new Properties ();
FileObject[] files = shortcutsFolder.getChildren ();
for (int i = 0; i < files.length; i++) {
String fName = files[i].getName ();
if (files[i].hasExt (KEYS_EXT) && !fName.equals(DEFAULT_KEYS_FILE) && !fName.equals(USER_KEYS_FILE)) {
props.put (fName, "Yes"); // NOI18N
}
}
props.store (propsFO.getOutputStream (propsLock), ""); // NOI18N
} catch (Exception e) {
if (Boolean.getBoolean ("netbeans.debug.exceptions")) e.printStackTrace(); // NOI18N
} finally {
if (propsLock != null) {
propsLock.releaseLock ();
}
}
}
public static void installCurrentBindings () {
try {
org.openide.filesystems.FileSystem systemFS = TopManager.getDefault().getRepository().getDefaultFileSystem ();
boolean defaultsUsed = false;
HashMap moduleKeyFiles = new HashMap (11);
FileObject mainFO = systemFS.find (SHORTCUTS_FOLDER, USER_KEYS_FILE, KEYS_EXT);
if (mainFO == null) {
defaultsUsed = true;
// if no user-defined shortcuts found, try to use the default ones
mainFO = systemFS.find (SHORTCUTS_FOLDER, DEFAULT_KEYS_FILE, KEYS_EXT);
}
// find all .keys files in the Shortcuts folder,
FileObject shortcutsFolder = FileUtil.createFolder (systemFS.getRoot (), SHORTCUTS_FOLDER);
FileObject[] files = shortcutsFolder.getChildren ();
for (int i = 0; i < files.length; i++) {
String fName = files[i].getName ();
if (files[i].hasExt (KEYS_EXT) && !fName.equals(DEFAULT_KEYS_FILE) && !fName.equals(USER_KEYS_FILE)) {
moduleKeyFiles.put (fName, files[i]);
}
}
if (!defaultsUsed) {
//
// in the case, when the user customized the shortcuts,
// we have to deal with the situation, that there might be modules
// installed after the time the user performed the customization
// and we now have to install their bindings as well
//
// the PROPERTIES_FILE file contains a list of all module keys files
// existing at the point of customization, i.e. their shortcuts are now contained
// in the USER_KEYS_FILE and should not be processed
//
FileObject propsFO = systemFS.find (SHORTCUTS_FOLDER, PROPERTIES_FILE, "properties"); // NOI18N
if (propsFO != null) {
Properties props = new Properties ();
try {
props.load (propsFO.getInputStream ());
} catch (IOException e) {
if (Boolean.getBoolean ("netbeans.debug.exceptions")) e.printStackTrace(); // NOI18N
}
// remove all files with name specified in the properties file from the list of files to process (moduleKeyFiles)
for (Enumeration items = props.keys(); items.hasMoreElements (); ) {
moduleKeyFiles.remove(items.nextElement());
}
}
}
HashMap allBindings = processKeysFiles (mainFO, moduleKeyFiles.values ());
installBindings (allBindings);
} catch (Exception e2) {
if (Boolean.getBoolean ("netbeans.debug.exceptions")) e2.printStackTrace(); // NOI18N
}
}
public static void installBindings (HashMap strokesMap) {
Keymap map = TopManager.getDefault ().getGlobalKeymap ();
map.removeBindings();
if (map instanceof NbKeymap) {
((NbKeymap)map).addActionForKeyStrokeMap(strokesMap);
} else {
// in the case we are working with unknown Keymap implementation,
// we have to add the items one by one
for (Iterator it = strokesMap.keySet().iterator(); it.hasNext (); ) {
KeyStroke key = (KeyStroke)it.next ();
map.addActionForKeyStroke(key, (Action) strokesMap.get (key));
}
}
}
/** Processes all files with shortcuts bindings and returns HashMap with all <KeyStroke, Action> mappings.
* @param mainKeysFile the main keys file with default bindings, can be null if there is no such file
* @param moduleKeyFiles collection of FileObjects with additional shortcuts
* @return HashMap of <KeyStroke, Action> mapping of all shortcut bindings
*/
private static HashMap processKeysFiles (FileObject mainKeysFile, Collection moduleKeysFiles) {
HashMap bindings = new HashMap (79);
if (mainKeysFile != null) {
try {
addBindings (parseKeysFile (mainKeysFile.getURL ()), bindings);
} catch (Exception e) {
if (Boolean.getBoolean ("netbeans.debug.exceptions")) e.printStackTrace(); // NOI18N
}
}
for (Iterator it = moduleKeysFiles.iterator (); it.hasNext (); ) {
try {
FileObject fo = (FileObject)it.next ();
addBindings (parseKeysFile (fo.getURL ()), bindings);
} catch (Exception e) {
if (Boolean.getBoolean ("netbeans.debug.exceptions")) e.printStackTrace(); // NOI18N
}
}
return bindings;
}
private static void addBindings (org.w3c.dom.Document doc, HashMap map) {
org.w3c.dom.NodeList nl = doc.getElementsByTagName (XML_BINDING);
for (int i = 0; i < nl.getLength (); i++) {
try {
String key = nl.item (i).getAttributes ().getNamedItem (ATTR_BINDING_KEY).getNodeValue ();
String act = nl.item (i).getAttributes ().getNamedItem (ATTR_BINDING_ACTION).getNodeValue ();
KeyStroke stroke = Utilities.stringToKey (key);
act = org.openide.util.Utilities.translate(act);
SystemAction action = SystemAction.get (Class.forName(act, true, TopManager.getDefault ().systemClassLoader ()));
map.put (stroke, action);
} catch (Exception e) { // NullPointer might be thrown if there is not the correct attribute present
if (Boolean.getBoolean ("netbeans.debug.exceptions")) e.printStackTrace(); // NOI18N
}
}
}
}
/*
* Log
* 19 Gandalf-post-FCS1.17.1.0 4/6/00 Ian Formanek Fixed bug 6104 - Can not
* change Editor Shortcut key Ctrl+A.
* 18 Gandalf 1.17 1/16/00 Jesse Glick Context help.
* 17 Gandalf 1.16 1/15/00 Jaroslav Tulach Should survive non
* existing Shortcuts folder and create it.
* 16 Gandalf 1.15 1/13/00 Ian Formanek I18N
* 15 Gandalf 1.14 1/9/00 Ian Formanek Removed debug printlns
* 14 Gandalf 1.13 1/9/00 Ian Formanek Improved saving of
* shortcuts
* 13 Gandalf 1.12 1/9/00 Ian Formanek Improved loading and
* adding shortcuts
* 12 Gandalf 1.11 1/4/00 Ian Formanek
* 11 Gandalf 1.10 1/4/00 Ian Formanek
* 10 Gandalf 1.9 1/2/00 Ian Formanek Form serialized in newer
* format
* 9 Gandalf 1.8 12/21/99 Ian Formanek
* 8 Gandalf 1.7 12/21/99 Ian Formanek Optimized adding
* multiple <action, shortcut> pairs into Keymap
* 7 Gandalf 1.6 12/1/99 Ian Formanek
* 6 Gandalf 1.5 11/30/99 Ian Formanek
* 5 Gandalf 1.4 11/30/99 Ian Formanek
* 4 Gandalf 1.3 11/29/99 Ian Formanek
* 3 Gandalf 1.2 11/26/99 Patrik Knakal
* 2 Gandalf 1.1 11/25/99 Ian Formanek
* 1 Gandalf 1.0 11/25/99 Ian Formanek
* $
*/